
from typing import Optional
import pandas as pd
import numpy as np
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import json
import configparser
import os
from collections import Counter
import ast
import webbrowser
import html_templates
from openai import OpenAI
import httpx
import re
#######
import pandas as pd

import requests
import numpy as np
import webbrowser  # 新增：用于打开浏览器
import os  # 新增：用于文件路径处理
from typing import Optional
import ssl
#print('开始启动，第一步查找表头所在的行数:')


# 示例函数abc - 当文件已存在且为同一文件时执行
def read_json(filename: str):
    #df = pd.read_json('./static/'+filename+'.json')
    df = pd.read_csv('./static/'+filename+'.json')
    #print(df,type(df),df.shape)
    return [df]
    # 这里可以添加实际业务逻辑

# 示例函数bcd - 当文件不存在时执行
def save2json(filename: str):
    print(f"执行函数bcd: 新文件 {filename} 已上传，")
    file_path='./static/'+filename
    df=read_excel_with_auto_header(file_path)
    # 写入 JSON 文件
    #df.to_json('./static/'+filename+'.json')
    df.to_csv('./static/'+filename+'.json')
    return df
    #columns_names=df.columns.tolist()
    # 这里可以添加实际业务逻辑

# 计算文件的MD5哈希值，用于判断是否为同一文件
def get_file_hash(file_path: str) -> str:
    hash_md5 = hashlib.md5()
    with open(file_path, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()


def read_config(config_file='config.ini'):
    print('读取config.ini配置信息')
    """读取配置文件并返回配置信息"""
    # 检查配置文件是否存在
    if not os.path.exists(config_file):
        raise FileNotFoundError(f"配置文件 {config_file} 不存在")
    
    # 创建配置解析器
    config = configparser.ConfigParser()
    
    # 读取配置文件
    config.read(config_file, encoding='utf-8')
    
    return config



def detect_header_row(file_path: str, max_rows_to_check: int = 20) -> Optional[int]:
    """智能检测Excel文件中表头所在的行索引（从0开始）"""
    # 读取可能包含表头的前N行数据
    df = pd.read_excel(file_path, nrows=max_rows_to_check, header=None)
    
    # 计算每行的字符串比例（字符串数量/总列数）
    string_ratio = []
    for i in range(len(df)):
        row = df.iloc[i]
        # 判断每个单元格是否为字符串类型
        is_string = row.apply(lambda x: isinstance(x, str) or (pd.notna(x) and not isinstance(x, (int, float, np.number))))
        ratio = sum(is_string) / len(row)
        string_ratio.append(ratio)
    
    # 计算每行的非空值比例
    non_null_ratio = df.notnull().sum(axis=1) / df.shape[1]
    
    # 计算每行的数值比例
    numeric_ratio = []
    for i in range(len(df)):
        row = df.iloc[i]
        is_numeric = pd.to_numeric(row, errors='coerce').notnull()
        ratio = sum(is_numeric) / len(row)
        numeric_ratio.append(ratio)
    
    # 表头通常具有较高的字符串比例和非空比例，较低的数值比例
    scores = []
    for i in range(len(df)):
        # 综合评分：字符串比例高、非空比例高、数值比例低的行更可能是表头
        score = string_ratio[i] * 0.4 + non_null_ratio[i] * 0.3 + (1 - numeric_ratio[i]) * 0.3
        scores.append(score)
    
    # 找到评分最高的行
    max_score = max(scores) if scores else 0
    candidate_row = scores.index(max_score) if scores else None
    
    # 简单验证：表头行通常不应全部为数值
    if candidate_row is not None:
        # 检查候选行是否大部分是字符串
        if string_ratio[candidate_row] > 0.5:
            # 检查下一行是否有更多数值（通常数据行包含更多数值）
            if candidate_row + 1 < len(scores) and numeric_ratio[candidate_row + 1] > numeric_ratio[candidate_row]:
                return candidate_row
    
    # 如果未找到明显的表头行，尝试寻找有唯一值最多的行
    unique_counts = []
    for i in range(len(df)):
        row = df.iloc[i]
        unique_count = row.nunique()
        unique_counts.append(unique_count)
    
    if unique_counts:
        max_unique = max(unique_counts)
        candidate_row = unique_counts.index(max_unique)
        if unique_counts[candidate_row] > len(df.columns) * 0.7:  # 至少70%的列有唯一值
            print('表头在{}行'.format(candidate_row+1))
            return candidate_row
    
    # 如果所有方法都失败，返回最可能的行（第一行）
    print('表头在{}行'.format(len(df)+1))
    return 0 if len(df) > 0 else None

def read_excel_with_auto_header(file_path: str) -> pd.DataFrame:
    """自动检测表头并读取Excel文件"""
    print('开始读取excel表格')
    header_row = detect_header_row(file_path)
    #print('header_row',header_row+1)

    df = pd.read_excel(file_path, header=header_row)

    return df

def local_LLM2json(user_prompt, system_prompt='',url='',TOKEN='',model='', ):
    config = read_config()
    url=config['local_LLM1']['url']
    TOKEN=config['local_LLM1']['token']
    model=config['local_LLM1']['model1']
    #大模型，直接回答，格式为json
    print('大模型回答问题',user_prompt,system_prompt)
    
    #url = config['llm1']['url']

    # 你的令牌
    #TOKEN = config['llm1']['token']

    payload = {
        "model": model,
        "messages": [
            {
                "role": "user",
                "content": user_prompt
            },
                        {
                "role": "system",
                "content": system_prompt
            }
        ]
    }
    headers = {
        "Authorization": f"Bearer {TOKEN}",  # 正确使用令牌变量
        "Content-Type": "application/json"
    }

    # 发送请求
    #print('url',url)
    #print('headers',headers)
    #print('payload',payload)
    response = requests.post(url, json=payload, headers=headers, verify=False)

    # 打印完整响应信息以便调试
    #print("响应状态码:", response.status_code)
    #print(dir(response))
    #print("响应状态码aaa:", response.text)
    #print("响应内容:", response.json())
    #print('response.json()',response.json())
    json_str=response.json()['choices'][0]['message']['content']
    json_str = json_str.replace('```json', '').replace('```', '').strip()
    def extract_braces_content(s):
        # 查找第一个大括号的位置
        start = s.find('{')
        # 查找最后一个大括号的位置
        end = s.rfind('}')
        
        # 检查是否找到有效的大括号对
        if start != -1 and end != -1 and start < end:
            # 返回从第一个大括号到最后一个大括号（包含）的子字符串
            return s[start:end+1]
        else:
            # 如果没有找到有效的大括号对，返回空字符串或None
            return ""
    json_str=extract_braces_content(json_str)
    print(json_str)
    #json.loads(json_str)['item']
    return json.loads(json_str)['item']






def get_filter_condition():
    """调用API获取筛选条件"""
    url = "https://api.siliconflow.cn/v1/chat/completions"
    TOKEN = "sk-zkqxvkdkhwkahwrydvjigsqkkykhaxedpmorwucaemypswid"  # 替换为你的实际令牌
    
    payload = {
        "model": "Qwen/Qwen2.5-Coder-7B-Instruct",
        "messages": [
            {
                "role": "user",
                "content": "写一段python pandas的代码，实现对年龄的筛选，要求45岁以下，且为女性，仅输出筛选条件即可，不要有多余内容"
            }
        ],
        "temperature": 0,
    }
    
    headers = {
        "Authorization": f"Bearer {TOKEN}",
        "Content-Type": "application/json"
    }
    
    # 发送请求
    response = requests.post(url, json=payload, headers=headers, verify=False)
    
    # 检查响应状态
    if response.status_code == 200:
        try:
            # 提取筛选条件
            content = response.json()['choices'][0]['message']['content']
            # 去除可能的代码标记
            return content.replace("```python", "").replace("```", "").strip()
        except Exception as e:
            print(f"解析响应出错: {e}")
            return None
    else:
        print(f"API请求失败，状态码: {response.status_code}")
        return None



def output_random_rows(dfs):
    # 获取第一个DataFrame
    df = dfs[0]
    
    # 确定要输出的行数（最多5行）
    num_rows = min(5, df.shape[0])
    
    # 随机选择行（如果行数小于等于5，则全部选择）
    if num_rows < 5:
        selected_rows = df
    else:
        # 生成不重复的随机索引
        random_indices = np.random.choice(df.index, size=num_rows, replace=False)
        selected_rows = df.loc[random_indices]
    
    # 输出表头和选中的行
    # 先输出表头
    #print(df.columns.tolist())
    # 再输出每行数据
    #for index, row in selected_rows.iterrows():
    #    print(row.tolist())
    
    return selected_rows.to_string()



def generate_prompt(dfs,question):
    random_rows=output_random_rows(dfs)
    #question="2025年客户联通有哪些订单"
    shape=str(dfs[0].shape)
    demo='''
    instruction <dataframe>
    dfs[0]:%s
    %s
    </dataframe>



    Update this initial code:
    ```python
    # TODO: import the required dependencies
    import pandas as pd

    # Write code here

    # Declare result var: 
    type (possible values "string", "number", "dataframe", "plot"). Examples: { "type": "string", "value": f"The highest salary is {highest_salary}." } or { "type": "number", "value": 125 } or { "type": "dataframe", "value": pd.DataFrame({...}) } or { "type": "plot", "value": "temp_chart.png" }

    ```



    ### QUERY
     %s

    Variable `dfs: list[pd.DataFrame]` is already declared.

    At the end, declare "result" variable as a dictionary of type and value.返回的类型必须是"dataframe"，求和的计算，如果数值类型是字符串，转成数字再计算

    If you are asked to plot a chart, use "matplotlib" for charts, save as png.


    Generate python code and return full updated code:
    '''
    return demo%(shape,random_rows,question)
def get_ai_python_code(dfs,question,base_url='https://api-inference.modelscope.cn/v1',api_key='ms-cc24cb3c-650c-4210-940d-c6007856adf2',model='Qwen/Qwen3-Next-80B-A3B-Instruct'):
    client = OpenAI(
        base_url=base_url,
        api_key=api_key,
        http_client=httpx.Client(verify=False)  # 直接禁用验证
    )



    prompt=generate_prompt(dfs,question)

    response = client.chat.completions.create(
        #model='Qwen/Qwen2.5-Coder-32B-Instruct',
        model=model,
        messages=[
            {'role': 'system', 'content': 'You are a helpful assistant.'},
            {'role': 'user', 'content': prompt}
        ],
        stream=False
    )

    return response.choices[0].message.content

def run_ai_generated_code(dfs, ai_generated_content):
    """
    提取并执行AI生成的Python代码，返回执行结果
    
    参数:
        dfs: 传入给生成代码的数据框列表
        ai_generated_content: AI返回的包含代码的完整内容
    
    返回:
        代码执行后得到的result变量值
    """
    # 1. 提取Python代码块
    code_pattern = re.compile(r'```python(.*?)```', re.DOTALL)
    match = code_pattern.search(ai_generated_content)
    
    if not match:
        raise ValueError("未在AI返回结果中找到Python代码块（需包含在```python和```之间）")
    
    # 提取并清理代码
    code = match.group(1).strip()
    print('python code:',code)
    print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
    
    # 2. 准备执行环境
    execution_namespace = {
        'dfs': dfs,
        'pd': pd,  # 预先导入pandas，避免生成代码中忘记导入
        'result': None
    }
    
    # 3. 执行代码并捕获异常
    try:
        exec(code, execution_namespace)
    except Exception as e:
        raise RuntimeError(f"代码执行失败: {str(e)}\n执行的代码:\n{code}")
    
    # 4. 检查并返回结果
    if 'result' not in execution_namespace:
        raise ValueError("生成的代码未定义'result'变量，请确保代码最后包含result定义")
    
    return execution_namespace['result']

def run_ai_generated_code1(dfs, ai_generated_content):
    """
    提取并执行AI生成的Python代码，返回执行结果
    
    参数:
        dfs: 传入给生成代码的数据框列表
        ai_generated_content: AI返回的包含代码的完整内容
    
    返回:
        代码执行后得到的result变量值
    """
    # 1. 提取Python代码块
    code_pattern = re.compile(r'```python(.*?)```', re.DOTALL)
    match = code_pattern.search(ai_generated_content)
    
    if not match:
        raise ValueError("未在AI返回结果中找到Python代码块（需包含在```python和```之间）")
    
    # 提取并清理代码
    code = match.group(1).strip()
    print('python code:',code)
    print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
    
    # 2. 准备执行环境
    execution_namespace = {
        'dfs': dfs,
        'pd': pd,  # 预先导入pandas，避免生成代码中忘记导入
        'result': None
    }
    
    # 3. 执行代码并捕获异常
    try:
        exec(code, execution_namespace)
    except Exception as e:
        raise RuntimeError(f"代码执行失败: {str(e)}\n执行的代码:\n{code}")
    
    # 4. 检查并返回结果
    if 'result' not in execution_namespace:
        raise ValueError("生成的代码未定义'result'变量，请确保代码最后包含result定义")
    
    return execution_namespace['result']

def dataframe_to_html_table(df: pd.core.frame.DataFrame, table_id: str = "dataframe-table", 
                           include_header: bool = True, add_styling: bool = True) -> str:
    """
    将pandas DataFrame转换为HTML表格字符串
    
    参数:
        df: 要转换的pandas DataFrame
        table_id: 生成的HTML表格的ID属性
        include_header: 是否包含表头
        add_styling: 是否添加基本的表格样式
    
    返回:
        包含表格的HTML字符串
    """
    # 开始构建HTML字符串
    html = []
    
    # 如果需要添加样式，先添加样式部分
    if add_styling:
        html.append(f"""<style>
            #{table_id} {{
                font-family: Arial, sans-serif;
                border-collapse: collapse;
                width: 100%;
                margin: 10px 0;
            }}
            #{table_id} th, #{table_id} td {{
                border: 1px solid #ddd;
                padding: 8px;
                text-align: left;
            }}
            #{table_id} tr:nth-child(even) {{
                background-color: #f2f2f2;
            }}
            #{table_id} th {{
                padding-top: 12px;
                padding-bottom: 12px;
                background-color: #165DFF;
                color: white;
            }}
        </style>""")
    
    # 开始表格标签
    html.append(f'<table id="{table_id}">')
    
    # 添加表头
    if include_header:
        html.append("  <thead>")
        html.append("    <tr>")
        for col in df.columns:
            html.append(f"      <th>{col}</th>")
        html.append("    </tr>")
        html.append("  </thead>")
    
    # 添加表格内容
    html.append("  <tbody>")
    for _, row in df.iterrows():
        html.append("    <tr>")
        for value in row:
            # 处理NaN值，显示为空
            display_value = "" if pd.isna(value) else str(value)
            html.append(f"      <td>{display_value}</td>")
        html.append("    </tr>")
    html.append("  </tbody>")
    
    # 结束表格标签
    html.append("</table>")
    
    # 合并所有部分并返回
    return "\n".join(html)

def get_ai(question,system_promtp='',base_url='https://api-inference.modelscope.cn/v1',api_key='ms-cc24cb3c-650c-4210-940d-c6007856adf2',model='Qwen/Qwen3-Next-80B-A3B-Instruct'):
    client = OpenAI(
        base_url=base_url,
        api_key=api_key,
        http_client=httpx.Client(verify=False)  # 直接禁用验证
    )



    prompt=question

    response = client.chat.completions.create(
        #model='Qwen/Qwen2.5-Coder-32B-Instruct',
        model=model,
        messages=[
            {'role': 'system', 'content': system_promtp},
            {'role': 'user', 'content': prompt}
        ],
        stream=False
    )

    return response.choices[0].message.content

def ai_requirement_python_run(requirment):
    result0=get_ai(requirment,system_promtp='你是一个python专家，善于理解用户的需求，能把用户需求转化为python代码，要求：根据用户需求生成完整的python代码。把需求写成一个函数叫requirement,input变量是用户的问题，return的结果"result"， variable as a dictionary of type and value.,最后执行这个函数,并打印结果')
    result=run_ai_generated_code1('',result0)
    return result
    

###############################################################
    


if __name__ == "__main__":
    print('库加载完成')
    #result0=get_ai('5分钟以后关机',system_promtp='你是一个python专家，善于理解用户的需求，能把用户需求转化为python代码，要求：根据用户需求生成完整的python代码。把需求写成一个函数叫requirement,input变量是用户的问题，return的结果"result"， variable as a dictionary of type and value.,最后执行这个函数,并打印结果')
##    code_pattern = re.compile(r'```python(.*?)```', re.DOTALL)
##    match = code_pattern.search(result0)
##    if not match:
##        raise ValueError("未在AI返回结果中找到Python代码块（需包含在```python和```之间）")
##    
##    # 提取并清理代码
##    code = match.group(1).strip()
##    print('python code:',code)
    #run_ai_generated_code1('',result0)
    ai_requirement_python_run('后天几号')
    print('end')
